use serde::{Deserialize, Deserializer, Serializer};
use serde_with::{DeserializeAs, SerializeAs};

use crate::prelude::*;
use crate::util;

/// Serializes the codecable type as a serde bytes instance.
pub struct AsCodec;

impl<T> SerializeAs<T> for AsCodec
where
    T: Encode,
{
    fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let buf = util::encode_to_vec(source).map_err(serde::ser::Error::custom)?;
        serializer.serialize_bytes(&buf)
    }
}

impl<'de, T> DeserializeAs<'de, T> for AsCodec
where
    T: for<'d> DecodeRef<'d>,
{
    fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
    where
        D: Deserializer<'de>,
    {
        let buf = serde_bytes::ByteBuf::deserialize(deserializer)?;
        util::decode_borrowed(&buf).map_err(serde::de::Error::custom)
    }
}

/// Serializees the codecable type as a hex string.
pub struct AsCodecHex;

impl<T> SerializeAs<T> for AsCodecHex
where
    T: Encode,
{
    fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let buf = util::encode_to_vec(source).map_err(serde::ser::Error::custom)?;
        hex::serde::serialize(&buf, serializer)
    }
}

impl<'de, T> DeserializeAs<'de, T> for AsCodecHex
where
    T: for<'d> DecodeRef<'d>,
{
    fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
    where
        D: Deserializer<'de>,
    {
        let buf = hex::serde::deserialize::<_, Vec<_>>(deserializer)?;
        util::decode_borrowed(&buf).map_err(serde::de::Error::custom)
    }
}
